/*
 * Copyright (c) 2022, IMMORTA Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice, this list
 *   of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright notice, this
 *   list of conditions and the following disclaimer in the documentation and/or
 *   other materials provided with the distribution.
 *
 * - Neither the name of IMMORTA Inc. nor the names of its
 *   contributors may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
/*!
 * @file dma_reg_access.h
 * @brief This file declares or defines dma register access functions
 */
#ifndef DMA_REG_ACCESS_H
#define DMA_REG_ACCESS_H

/*******Includes***************************************************************/
#include "dma_drv.h"

/*******Definitions************************************************************/
#define DMA_GLB_ENABLE(x) (((uint32_t)(((uint32_t)(x)) << DMA_GLB_ENABLE_ENABLE_Pos)) & DMA_GLB_ENABLE_ENABLE_Msk)

#define DMA_CHCR_SI(x) (((uint32_t)(((uint32_t)(x)) << DMA_CHCR_SI_Pos)) & DMA_CHCR_SI_Msk)
#define DMA_CHCR_DI(x) (((uint32_t)(((uint32_t)(x)) << DMA_CHCR_DI_Pos)) & DMA_CHCR_DI_Msk)
#define DMA_CHCR_LLIITC(x) (((uint32_t)(((uint32_t)(x)) << DMA_CHCR_LLIITC_Pos)) & DMA_CHCR_LLIITC_Msk)

#define DMA_CHCONF_TCIE(x) (((uint32_t)(((uint32_t)(x)) << DMA_CHCONF_TCIE_Pos)) & DMA_CHCONF_TCIE_Msk)
#define DMA_CHCONF_TEIE(x)  (((uint32_t)(((uint32_t)(x)) << DMA_CHCONF_TEIE_Pos))  & DMA_CHCONF_TEIE_Msk)
#define DMA_CHCONF_ENABLE(x) (((uint32_t)(((uint32_t)(x)) << DMA_CHCONF_ENABLE_Pos)) & DMA_CHCONF_ENABLE_Msk)
#define DMA_CHCONF_HALT(x) (((uint32_t)(((uint32_t)(x)) << DMA_CHCONF_HALT_Pos)) & DMA_CHCONF_HALT_Msk)


/*******APIs*******************************************************************/
#if defined(__cplusplus)
extern "C" {
#endif

/*!
 * @brief DMA module enable
 *
 * @param[in] regBase: The DMA register base address
 * @param[in] enable: Enable flag
 *            - true: Enable the DMA module
 *            - false: Disable the DMA module
 * @return None
 */
static inline void DMA_REG_SetEnable(DMA_Type* regBase, bool enable)
{
    regBase->GLB_ENABLE = (regBase->GLB_ENABLE & ~DMA_GLB_ENABLE_ENABLE_Msk) | DMA_GLB_ENABLE(enable ? 1U : 0U);
}

/*!
 * @brief Which channels are enabled
 *
 * @param[in] regBase: The DMA channel register base address
 * @return Channels that has been enabled
 */
static inline uint8_t DMA_REG_GetChannleEnabled(const DMA_Type* regBase)
{
    return (regBase->CH_ENABLE & DMA_CH_ENABLE_CH_ENABLE_Msk) >> DMA_CH_ENABLE_CH_ENABLE_Pos;
}

/*!
 * @brief Initializes the DMA channel default configuration
 *
 * @param[in] regBase: The DMA channel register base address
 * @return None
 */
static inline void DMA_REG_ChannelInit(DMA_CH_Type* regBase)
{
    regBase->CHCONF = 0U;
    regBase->CHSRC = 0U;
    regBase->CHDST = 0U;
    regBase->CHCR = 0U;
    regBase->CHLLI = 0U;
    regBase->CHSR = 3U;
}

/*!
 * @brief Set transfer type
 *
 * @param[in] regBase: The DMA channel register base address
 * @param[in] type: Transfer type(M->M,M->P,P->M,P->P)
 * @return None
 */
static inline void DMA_REG_SetTransferType(DMA_CH_Type* regBase, dma_transfer_type_t type)
{
    uint32_t regValue = regBase->CHCONF;
    regValue &= ~(DMA_CHCONF_FLOWCTRL_Msk);
    regValue |= ((uint32_t)type << DMA_CHCONF_FLOWCTRL_Pos) & DMA_CHCONF_FLOWCTRL_Msk;
    regBase->CHCONF = regValue;
}

/*!
 * @brief Set DMA source address
 *
 * @param[in] regBase: The DMA channel register base address
 * @param[in] address: Source address
 * @return None
 */
static inline void DMA_REG_SetSrcAddr(DMA_CH_Type* regBase, uint32_t address)
{
    regBase->CHSRC = address;
}

/*!
 * @brief Set DMA destination address
 *
 * @param[in] regBase: The DMA channel register base address
 * @param[in] address: Destination address
 * @return None
 */
static inline void DMA_REG_SetDestAddr(DMA_CH_Type* regBase, uint32_t address)
{
    regBase->CHDST = address;
}

/*!
 * @brief Set Source address increment.
 *
 * @param[in] regBase: The DMA channel register base address
 * @param[in] increment: When set true, the source address is incremented after each transfer.
 * @return None
 */
static inline void DMA_REG_SetSrcAddrIncrement(DMA_CH_Type* regBase, bool increment)
{
    regBase->CHCR = (regBase->CHCR & ~DMA_CHCR_SI_Msk) | DMA_CHCR_SI(increment ? 1U : 0U);
}

/*!
 * @brief Set destination address increment.
 *
 * @param[in] regBase: The DMA channel register base address
 * @param[in] increment: When set true, the destination address is incremented after each transfer.
 * @return None
 */
static inline void DMA_REG_SetDestAddrIncrement(DMA_CH_Type* regBase, bool increment)
{
    regBase->CHCR = (regBase->CHCR & ~DMA_CHCR_DI_Msk) | DMA_CHCR_DI(increment ? 1U : 0U);
}

/*!
 * @brief Set source transfer data width(8/16/32bits).
 *
 * @param[in] regBase: The DMA channel register base address
 * @param[in] width: Source transfer data width
 * @return None
 */
static inline void DMA_REG_SetSrcTransferWidth(DMA_CH_Type* regBase, dma_transfer_width_t width)
{
    uint32_t regValue = regBase->CHCR;
    regValue &= ~(DMA_CHCR_SWIDTH_Msk);
    regValue |= ((uint32_t)width << DMA_CHCR_SWIDTH_Pos) & DMA_CHCR_SWIDTH_Msk;
    regBase->CHCR = regValue;
}

/*!
 * @brief Set destination transfer data width(8/16/32bits).
 *
 * @param[in] regBase: The DMA channel register base address
 * @param[in] width: Destination transfer data width
 * @return None
 */
static inline void DMA_REG_SetDestTransferWidth(DMA_CH_Type* regBase, dma_transfer_width_t width)
{
    uint32_t regValue = regBase->CHCR;
    regValue &= ~(DMA_CHCR_DWIDTH_Msk);
    regValue |= ((uint32_t)width << DMA_CHCR_DWIDTH_Pos) & DMA_CHCR_DWIDTH_Msk;
    regBase->CHCR = regValue;
}

/*!
 * @brief Set source peripheral request id.
 *
 * @param[in] regBase: The DMA channel register base address
 * @param[in] request: This value selects the DMA source request peripheral.
 * @return None
 */
static inline void DMA_REG_SetSrcPeriphRequest(DMA_CH_Type* regBase, dma_perial_request_t request)
{
    uint32_t regValue = regBase->CHCONF;
    regValue &= ~(DMA_CHCONF_SRCPERIPH_Msk);
    regValue |= ((uint32_t)request << DMA_CHCONF_SRCPERIPH_Pos) & DMA_CHCONF_SRCPERIPH_Msk;
    regBase->CHCONF = regValue;
}

/*!
 * @brief Set destination peripheral request id.
 *
 * @param[in] regBase: The DMA channel register base address
 * @param[in] request: This value selects the DMA destination request peripheral.
 * @return None
 */
static inline void DMA_REG_SetDestPeriphRequest(DMA_CH_Type* regBase, dma_perial_request_t request)
{
    uint32_t regValue = regBase->CHCONF;
    regValue &= ~(DMA_CHCONF_DESTPERIPH_Msk);
    regValue |= ((uint32_t)request << DMA_CHCONF_DESTPERIPH_Pos) & DMA_CHCONF_DESTPERIPH_Msk;
    regBase->CHCONF = regValue;
}

/*!
 * @brief Set transfer size
 *
 * @param[in] regBase: The DMA channel register base address
 * @param[in] transferSize: Transfer size, write to this value sets the size of the transfer
 * @return None
 */
static inline void DMA_REG_SetTransferSize(DMA_CH_Type* regBase, uint32_t transferSize)
{
    uint32_t regValue = regBase->CHCR;
    regValue &= ~(DMA_CHCR_TRANSFERSIZE_Msk);
    regValue |= ((uint32_t)transferSize << DMA_CHCR_TRANSFERSIZE_Pos) & DMA_CHCR_TRANSFERSIZE_Msk;
    regBase->CHCR = regValue;
}

/*!
 * @brief Set linked list item register
 *
 * @param[in] regBase: The DMA channel register base address
 * @param[in] linkedList: Linked list item pointer
 * @return None
 */
static inline void DMA_REG_SetLinkedList(DMA_CH_Type* regBase, const dma_linked_list_item_t* linkedList)
{
    regBase->CHLLI = (uint32_t)linkedList;
}

/*!
 * @brief Set transfer complete interrupt enable or disable
 *
 * @param[in] regBase: The DMA channel register base address
 * @param[in] enable: Enable flag
 *            - true: Enable the transfer complete interrupt
 *            - false: Disable the transfer complete interrupt
 * @return None
 */
static inline void DMA_REG_SetCompleteIntEnable(DMA_CH_Type* regBase, bool enable)
{
    regBase->CHCONF = (regBase->CHCONF & ~DMA_CHCONF_TCIE_Msk) | DMA_CHCONF_TCIE(enable ? 1U : 0U);
}

/*!
 * @brief Whether the transfer completion interrupt is enabled
 *
 * @param[in] regBase: The DMA channel register base address
 * @return true: Transfer completion interrupt is enabled
 *         false: Transfer completion interrupt is disabled
 */
static inline bool DMA_REG_IsCompleteIntEnabled(const DMA_CH_Type* regBase)
{
    return (regBase->CHCONF & DMA_CHCONF_TCIE_Msk) >> DMA_CHCONF_TCIE_Pos;
}

/*!
 * @brief Set transfer error interrupt enable or disable
 *
 * @param[in] regBase: The DMA channel register base address
 * @param[in] enable: Enable flag
 *            - true: Enable the transfer error interrupt
 *            - false: Disable the transfer error interrupt
 * @return None
 */
static inline void DMA_REG_SetErrorIntEnable(DMA_CH_Type* regBase, bool enable)
{
    regBase->CHCONF = (regBase->CHCONF & ~DMA_CHCONF_TEIE_Msk) | DMA_CHCONF_TEIE(enable ? 1U : 0U);
}

/*!
 * @brief Whether the transfer error interrupt is enabled
 *
 * @param[in] regBase: The DMA channel register base address
 * @return true: Transfer error interrupt is enabled
 *         false: Transfer error interrupt is disabled
 */
static inline bool DMA_REG_IsErrorIntEnabled(const DMA_CH_Type* regBase)
{
    return (regBase->CHCONF & DMA_CHCONF_TEIE_Msk) >> DMA_CHCONF_TEIE_Pos;
}

/*!
 * @brief Whether the transfer is complete
 *
 * @param[in] regBase: The DMA channel register base address
 * @return true: Transfer is complete
 *         false: Transfer not complete
 */
static inline bool DMA_ERG_IsTransferCompleted(const DMA_CH_Type* regBase)
{
    return (regBase->CHSR & DMA_CHSR_DONE_Msk) >> DMA_CHSR_DONE_Pos;
}

/*!
 * @brief Whether a transmission error has occurred
 *
 * @param[in] regBase: The DMA channel register base address
 * @return true: A transmission error occurred
 *         false: No transmission errors occurred
 */
static inline bool DMA_ERG_IsTransferError(const DMA_CH_Type* regBase)
{
    return (regBase->CHSR & DMA_CHSR_ERR_Msk) >> DMA_CHSR_ERR_Pos;
}

/*!
 * @brief Clear dma transfer complete status flag
 *
 * @param[in] regBase: The DMA channel register base address
 * @return None
 */
static inline void DMA_ERG_ClearCompletedIntStatus(DMA_CH_Type* regBase)
{
    regBase->CHSR = 1U << DMA_CHSR_DONE_Pos;
}

/*!
 * @brief Clear dma transfer error status flag
 *
 * @param[in] regBase: The DMA channel register base address
 * @return None
 */
static inline void DMA_ERG_ClearErrorIntStatus(DMA_CH_Type* regBase)
{
    regBase->CHSR = 1U << DMA_CHSR_ERR_Pos;
}

/*!
 * @brief Set DMA channel enable
 *
 * @param[in] regBase: The DMA channel register base address
 * @param[in] enable: Enable flag
 *            - true: Enable the channel
 *            - false: Disable the channel
 * @return None
 */
static inline void DMA_REG_SetChannleEnable(DMA_CH_Type* regBase, bool enable)
{
    regBase->CHCONF = (regBase->CHCONF & ~DMA_CHCONF_ENABLE_Msk) | DMA_CHCONF_ENABLE(enable ? 1U : 0U);
}

/*!
 * @brief Set DMA channel halt
 *
 * @param[in] regBase: The DMA channel register base address
 * @param[in] enable: Enable flag
 *            - true: Hatl the channel transfer, ignore subsequent source DMA requests
 *            - false: Resume the channel transfer, enable DMA requests
 * @return None
 */
static inline void DMA_REG_SetHaltEnable(DMA_CH_Type* regBase, bool enable)
{
    regBase->CHCONF = (regBase->CHCONF & ~DMA_CHCONF_HALT_Msk) | DMA_CHCONF_HALT(enable ? 1U : 0U);
}

/*!
 * @brief Set DMA channel lli itc enable
 *
 * @param[in] regBase: The DMA channel register base address
 * @param[in] enable: It controls whether the current LLI is expected to trigger the terminal count interrupt
 * @return None
 */
static inline void DMA_REG_SetLLIITCEnable(DMA_CH_Type* regBase, bool enable)
{
    regBase->CHCR = (regBase->CHCR & ~DMA_CHCR_LLIITC_Msk) | DMA_CHCR_LLIITC(enable ? 1U : 0U);
}

/*!
 * @brief Whether the FIFO of the channel has data
 *
 * @param[in] regBase: The DMA channel register base address
 * @return true: The FIFO of the channel has data
 *         false: There is no data in the FIFO of the channel
 */
static inline bool DMA_REG_IsFifoleftData(const DMA_CH_Type* regBase)
{
    return (regBase->CHCONF & DMA_CHCONF_ACTIVE_Msk) >> DMA_CHCONF_ACTIVE_Pos;
}

/*!
 * @brief Get transfer size
 *
 * @param[in] regBase: The DMA channel register base address
 * @return: The number of transfers left to complete.
 */
static inline uint32_t DMA_REG_GetTransferSize(const DMA_CH_Type* regBase)
{
    return (regBase->CHCR & DMA_CHCR_TRANSFERSIZE_Msk) >> DMA_CHCR_TRANSFERSIZE_Pos;
}

/*!
 * @brief Generate a DMA request for each source by writing a 1 to the corresponding register bit.
 *
 * @param[in] regBase: The DMA channel register base address
 * @param[in] request: The DMA request
 * @return None
 */
static inline void DMA_REG_TriggerSwRequest(DMA_Type* regBase, dma_perial_request_t request)
{
    uint32_t regValue = regBase->SWREQ;
    regValue &= ~(DMA_SWREQ_SWREQ_Msk);
    regValue |= (1U << (uint8_t)request) & DMA_SWREQ_SWREQ_Msk;
    regBase->SWREQ = regValue;
}

#if defined(__cplusplus)
}
#endif

#endif /* DMA_REG_ACCESS_H */

/*******EOF********************************************************************/
